package com.stone.threadpool;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ThreadPoolExecutor 线程池简单版本
 */
public class StoneThreadPool {

    /**
     * 核心线程数
     */
    private volatile int corePoolSize;

    /**
     * 最大线程数
     */
    private volatile int maximumPoolSize;

    /**
     * 最大存活时间
     */
    private volatile long keepAliveTime;

    /**
     * 最大线程数
     */
    BlockingQueue<ITask> workQueue;

    /**
     * 用来记录当前线程池中在运行的线程数
     */
    private final AtomicInteger ctl = new AtomicInteger(0);

    public StoneThreadPool(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<ITask> workQueue) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.workQueue = workQueue;
    }

    /**
     * 提交任务的方法
     *
     * @param command
     */
    public void execute(ITask command) {
        if (command == null) {
            throw new NullPointerException();
        }
        // 拿到当前池子里正在运行的线程数量
        int currentNum = ctl.get();
        // 如果正在运行的线程数量小于核心线程，则直接创建线程去执行
        if (currentNum < corePoolSize) {
            // 如果现在是第一个任务，我要去执行任务，并且开启新线程
            if (addWorker(command, true)) {
                return;
            }
        }
        // 如果正在运行的线程数不小于核心线程，则把任务往队列里面放
        if (workQueue.offer(command)) {
            // 啥都不用管
        }
        // 如果在运行的线程达到了核心线程了，且队列也放满了，就常识添加非核心线程
        else if (!addWorker(command, false)) {
            // 如果线程数达到最大了，队列也满了则执行拒绝策略
            System.err.println("拒绝你");
        }
    }

    public boolean addWorker(ITask firstTask, boolean core) {
        try {
            if (ctl.get() >= (core ? corePoolSize : maximumPoolSize)) {
                return false;
            }
            final Thread t = new Thread(new Worker(firstTask));
            ctl.getAndIncrement();
            if (t != null) {
                t.start();
            }
        } finally {

        }
        return true;
    }

    private final class Worker implements Runnable {

        private static final long serialVersionUID = 2443525235345353L;

        ITask firstTask;

        Worker(ITask firstTask) {
            this.firstTask = firstTask;
        }

        @Override
        public void run() {
            runWorker(this);
        }
    }

    final void runWorker(Worker w) {
        ITask task = w.firstTask;
        w.firstTask = null;
        try {
            while (task != null || (task = getTask()) != null) {
                try {
                    task.execute();
                } finally {
                    task = null;
                }
            }
        } finally {

        }
    }

    private ITask getTask() {
        for (; ; ) {
            int wc = ctl.get();
            boolean timed = wc > corePoolSize;
            try {
                ITask r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null) {
                    return r;
                }
            } catch (InterruptedException e) {

            }
        }
    }
}
